/*!
\copyright  Copyright (c) 2023 - 2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
    \ingroup    le_unicast_manager
\brief      The le unicast voice profile audio params
*/

#if defined(INCLUDE_LE_AUDIO_UNICAST)

#include "le_unicast_voice_param.h"
#include "le_unicast_manager_instance.h"
#include "micp_server.h"
#include "voice_sources_list.h"

#include <panic.h>
#include <logging.h>

static uint16 leUnicastVoiceSource_GetFrameLength(le_um_ase_t *ase)
{
    uint8 frame_blocks_per_sdu = LeUnicastManager_GetCodecFrameBlocksPerSdu(ase->codec_info);

    return LeUnicastManager_GetFramelength(ase->qos_info->maximumSduSize, 
                                                            frame_blocks_per_sdu, LeUnicastManager_GetAudioLocation(ase->codec_info));
}

static inline appKymeraLeCodecId leUnicastVoiceSource_CopyCodecId(GattAscsCodecId gatt_codec_id)
{
    appKymeraLeCodecId codec_id = {
                                .codingFormat = gatt_codec_id.codingFormat,
                                .companyId = gatt_codec_id.companyId,
                                .vendorSpecificCodecId = gatt_codec_id.vendorSpecificCodecId
                                  };
    return codec_id;
}

/* \brief Extract audio parameters from Codec and Qos Information */
bool LeUnicastVoiceSource_ExtractAudioParameters(le_um_instance_t *inst,
                                                        le_voice_connect_parameters_t *conn_param)
{
    bool populate_success = FALSE;
    le_um_ase_t *sink_ase;
    le_um_ase_t *source_ase;

    le_um_ase_t *sink_ase_r = LeUnicastManager_InstanceGetRightSinkAse(inst);
    multidevice_side_t side = Multidevice_GetSide();

    UNICAST_MANAGER_LOG("leUnicastVoiceSource_ExtractAudioParameters :");

    LeUnicastManager_GetAsesForGivenSide(inst, side, &sink_ase, &source_ase);

    /* To prevent non-zero, garbage values during initialization in handle */
    conn_param->speaker.iso_handle = LE_AUDIO_INVALID_ISO_HANDLE;
    conn_param->speaker.iso_handle_right = LE_AUDIO_INVALID_ISO_HANDLE;
    conn_param->microphone.source_iso_handle = LE_AUDIO_INVALID_ISO_HANDLE;
    conn_param->microphone.source_iso_handle_right = LE_AUDIO_INVALID_ISO_HANDLE;
    conn_param->microphone_present = FALSE;
    conn_param->speaker_present = FALSE;
    conn_param->reconfig = TRUE;

    /* Fill in the Audio Parameters */
    if (sink_ase != NULL && (sink_ase->state == le_um_ase_state_streaming || sink_ase->state == le_um_ase_state_routed))
    {
        LeUnicastManager_ExtractVoiceSinkIsoHandles(inst,
                                                  sink_ase,
                                                  &conn_param->speaker.iso_handle,
                                                  &conn_param->speaker.iso_handle_right);

        conn_param->volume = VoiceSources_CalculateOutputVolume(LeUnicastManager_GetVoiceSourceForInstance(inst));
        conn_param->speaker.sample_rate = LeUnicastManager_GetSampleRate(sink_ase->codec_info);
        conn_param->speaker.frame_duration = LeUnicastManager_GetFrameDuration(sink_ase);
        conn_param->speaker.codec_type = KYMERA_LE_AUDIO_CODEC_LC3; /* @TODO this has to be derived by application from the ASE configuration data. */
        conn_param->speaker.codec_version = sink_ase->codec_version;
        conn_param->speaker_present = TRUE;

        conn_param->speaker.codec_frame_blocks_per_sdu = LeUnicastManager_GetCodecFrameBlocksPerSdu(sink_ase->codec_info);
        conn_param->speaker.frame_length = leUnicastVoiceSource_GetFrameLength(sink_ase);
        conn_param->speaker.presentation_delay = sink_ase->qos_info->presentationDelay;
        conn_param->speaker.stream_type = leUnicastManager_DetermineStreamType(sink_ase,
                                                                               (sink_ase_r != NULL && leUnicastManager_IsAseActive(sink_ase_r)) ?
                                                                                sink_ase_r : NULL);
        sink_ase->state = le_um_ase_state_routed;

        populate_success = TRUE;
        UNICAST_MANAGER_LOG("Speaker path iso_handle : 0x%x, iso_handle_right : 0x%x, Volume %d",
                            conn_param->speaker.iso_handle,
                            conn_param->speaker.iso_handle_right,
                            conn_param->volume.value);

        UNICAST_MANAGER_LOG(" sample_rate : %d, frame_duration %d, codec_type: %d, codec_version: %d",
                            conn_param->speaker.sample_rate,
                            conn_param->speaker.frame_duration,
                            conn_param->speaker.codec_type,
                            conn_param->speaker.codec_version);
    }

    if (source_ase != NULL)
    {
        le_um_cis_t *mic_cis_info = source_ase->cis_data;
        conn_param->microphone.source_iso_handle = mic_cis_info->cis_handle;
        conn_param->microphone.mic_mute_state = (uint8)MicpServer_GetMicState();

        conn_param->microphone.codec_frame_blocks_per_sdu = LeUnicastManager_GetCodecFrameBlocksPerSdu(source_ase->codec_info);
        conn_param->microphone.frame_length = leUnicastVoiceSource_GetFrameLength(source_ase);
        conn_param->microphone.presentation_delay = source_ase->qos_info->presentationDelay;
        conn_param->microphone.sample_rate = LeUnicastManager_GetSampleRate(source_ase->codec_info);
        conn_param->microphone.frame_duration = LeUnicastManager_GetFrameDuration(source_ase);
        conn_param->microphone.codec_type = KYMERA_LE_AUDIO_CODEC_LC3; /* @TODO this has to be derived by application from the ASE configuration data. */
        conn_param->microphone.codec_id = leUnicastVoiceSource_CopyCodecId(source_ase->codec_info->codecId);
        conn_param->microphone.codec_version = source_ase->codec_version;
        conn_param->microphone_present = TRUE;

        populate_success = TRUE;

        /* Don't move source ASE state to streaming as it needs to be done only after
         * getting ReadyToReceive unicast client
         */
        source_ase->state = le_um_ase_state_routed;
        UNICAST_MANAGER_LOG("Microphone path frame_length: %d, Presentation delay: %d, Sample Rate %d, Frame duration %d, codec_type %d, Codec Version %d, Frame Blocks Per SDU %d",
                    conn_param->microphone.frame_length,
                    conn_param->microphone.presentation_delay,
                    conn_param->microphone.sample_rate,
                    conn_param->microphone.frame_duration,
                    conn_param->microphone.codec_type,
                    conn_param->microphone.codec_version,
                    conn_param->microphone.codec_frame_blocks_per_sdu);
        UNICAST_MANAGER_LOG("Microphone path iso_handle: 0x%x", conn_param->microphone.source_iso_handle);
    }

    return  populate_success;
}

bool LeUnicastVoiceSource_GetAudioConnectParameters(voice_source_t source, source_defined_params_t *source_params)
{
    bool populate_success = FALSE;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByVoiceSource(source);

    UNICAST_MANAGER_LOG("LeUnicastVoiceSource_GetAudioConnectParameters");

    if (inst)
    {
        le_voice_connect_parameters_t *conn_param =
            (le_voice_connect_parameters_t *) PanicUnlessMalloc(sizeof(le_voice_connect_parameters_t));
        memset(conn_param, 0, sizeof(le_voice_connect_parameters_t));

        conn_param->reconfig = FALSE;
        if (LeUnicastVoiceSource_ExtractAudioParameters(inst, conn_param))
        {
            source_params->data = (void *)conn_param;
            source_params->data_length = sizeof(le_voice_connect_parameters_t);
            populate_success = TRUE;
        }
        else
        {
            /* Free the allocated voice connect parameter */
            pfree(conn_param);
        }
    }

    UNICAST_MANAGER_LOG("LeUnicastVoiceSource_GetAudioConnectParameters success %d", populate_success);
    return populate_success;
}

void LeUnicastVoiceSource_FreeAudioConnectParameters(voice_source_t source,
                                                            source_defined_params_t *source_params)
{
    if (VoiceSource_IsLeUnicastSource(source))
    {
        PanicNull(source_params);
        PanicFalse(source_params->data_length == sizeof(le_voice_connect_parameters_t));

        pfree(source_params->data);
        source_params->data = (void *)NULL;
        source_params->data_length = 0;
    }
}

bool LeUnicastVoiceSource_GetAudioDisconnectParameters(voice_source_t source,
                                                              source_defined_params_t *source_params)
{
    UNUSED(source);

    PanicNull(source_params);
    source_params->data = (void *)NULL;
    source_params->data_length = 0;

    return TRUE;
}

void LeUnicastVoiceSource_FreeAudioDisconnectParameters(voice_source_t source,
                                                               source_defined_params_t *source_params)
{
    UNUSED(source);

    PanicNull(source_params);
    source_params->data = (void *)NULL;
    source_params->data_length = 0;;
}

#endif /* defined(INCLUDE_LE_AUDIO_UNICAST) */
